#pragma region Include
#include <Windows.h>
#include <string.h>
#include <stdio.h>
#include <list>

#include "debug.h"

#include "parser.h"
#pragma endregion Include

using namespace std;

namespace id
	{
		enum methods_en{get = 0, post, options, head, put, _delete, trace,connect};
	}
const char *methods[] = {"GET", "POST", "OPTIONS", "HEAD", 
							"PUT", "DELETE", "TRACE", "CONNECT", NULL};
namespace id
	{
		enum fields_en{host = 0, user_agent, cookie, accept, accept_encoding, 
			accept_language, accept_charset, content_length};
	}
const char *fields[] = {"Host:", "User-Agent:", "Cookie:", "Accept:", "Accept-Encoding:",
							"Accept-Languge:", "Accept-Charset:", "Content-Length:", NULL};

id::methods_en operator++(id::methods_en &val)
{
	int ti_val = 0;
	//id::methods_en tm_val = val;

	ti_val = (int)val;
	ti_val++;
	val = (id::methods_en)ti_val;

	return val;
}

id::fields_en operator++(id::fields_en &val)
{
	int ti_val = 0;

	ti_val = (int)val;
	ti_val++;
	val = (id::fields_en)ti_val;

	return val;
}

int get_size_array(const char **arr);


CHttpParser::CHttpParser()
{
	header_buf_alloc = false;
	header_buf = NULL;

	memset(method, 0, 10);
	memset(URI, 0, MAX_BUF);
	memset(params, 0, MAX_BUF);
	memset(host, 0, MAX_BUF);
	memset(user_agent, 0, MAX_BUF);
	memset(cookie, 0, MAX_BUF);
	memset(accept, 0, MAX_BUF);
	memset(accept_encoding, 0, MAX_BUF);
	memset(accept_language, 0, MAX_BUF);
	memset(accept_charset, 0, MAX_BUF);
	content_length = 0;
}

CHttpParser::~CHttpParser()
{
	if(!header_buf_alloc)
		return;
	
	delete [] header_buf;

	header_buf_alloc = false;
}

char *CHttpParser::get_header(const char *buf)
{
	int len = 0;

	clear();

	len = this->length_header(buf);

	if(!len)
		return 0;

	header_buf = new char[len + 1];

	memset(header_buf,0,len);
	header_buf_alloc = true;

	for(int i = 0; i<=len; i++)
		this->header_buf[i] = buf[i];
	
	//printf("%s\n",this->header_buf);

	return header_buf;
}

int CHttpParser::length_header(const char *buf)
{
	int count = 0;
	
	if(!strlen(buf))
		return 0;

	while(true)
	{
		if(buf[count] == '\r' && buf[count + 1] == '\n' &&
			buf[count + 2] == '\r' && buf[count + 3] == '\n')
				break;			
		count++;
	}

	return count + 4;
}

char *CHttpParser::get_data(const char *buf)
{
	char p = 0;
	long count = 0;

	while(buf[count])
		count++;

	memset(params, 0, MAX_BUF);
	memcpy(params, buf + count - content_length, content_length); 

	return params;
}

int CHttpParser::parse_header(const char *buf)
{
	char *token = NULL;
	char delims[] = "\r\n";
	
	list<char*> lst_str;
	list<char*>::iterator iter = lst_str.begin();

	token = strtok(header_buf,delims);
	//printf("%s\n",token);
	lst_str.push_back(token);

	while(token)
	{
		token = strtok(NULL,delims);
		//printf("%s\n",token);
		lst_str.push_back(token);
	}

	for(iter = lst_str.begin(); iter != lst_str.end(); iter++)
	{
		token = *iter;

		if(token == NULL)
			break;
		if(is_method(token))
			parse_method(token);
		if(is_field(token))
			parse_field(token);
	}

	//printf("%d\n", get_size_array(methods));

	return 0;
}

int CHttpParser::is_method(const char *str)
{
	int len_array = get_size_array(methods);

	for(int i = 0; i<=len_array; i++)
		if(strstr(str, methods[i]))
			return true;

	return false;
}

int CHttpParser::parse_method(const char *str)
{
	char *token = NULL;
	char delims[] = " \r\n";

	char *t_str = new char[strlen(str) + 1];

	id::methods_en i = id::get;
	
	strcpy(t_str, str);
	token = strtok(t_str, delims);

	for(; i <= id::connect; i++)
	{
		if(methods[i] == 0)
			break;
		if(strcmp(token, methods[i]) == 0)
		{
			strcpy(this->method, methods[i]);
			
			token = strtok(NULL,delims);
			if(strlen(token) > MAX_BUF)
				return 1;
			//strcpy(this->URI, token);
			this->parse_URL(token);

			break;
		}
	}

	//while(strtok(NULL,delims));

	delete [] t_str;

	return 0;
}

void CHttpParser::parse_URL(const char *URL)
{
	int len = strlen(URL) + 1, pos = 0;
	bool fl = false;

	for(int i = 0; i<len; i++)
	{
		if(fl == false)
		{
			this->URI[i] = URL[i];
			if(URL[i] == '?')
			{
				URI[i] = '\0';
				pos = i + 1;
				fl = true;
			}
		}
		else
			this->params[i - pos] = URL[i];
	}
}

int CHttpParser::is_field(const char *str)
{
	int len_array = get_size_array(fields);
	char *p = NULL;

	for(int i = 0; i<=len_array; i++)
	{
		p = strstr((char*)str,fields[i]);
		if(p)
			return true;
	}

	return 0;
}

int CHttpParser::parse_field(const char *str)
{
	char *token = NULL;
	char delims[] = " \r\n";

	char *t_str = new char[strlen(str) + 1];

	id::fields_en i = id::host;

	strcpy(t_str, str);
	token = strtok(t_str, delims);

	for(; i<= id::content_length; i++)
	{
		if(fields[i] == 0)
			break;
		if(strcmp(token, fields[i]) == 0)
		{
			token = strtok(NULL,delims);
			if(strlen(token) > MAX_BUF)
				return 1;
			
			switch(i)
			{
			case id::host:
				strcpy(this->host, token);
				break;
			case id::user_agent:
				strcpy(this->user_agent, token);
				break;
			case id::cookie:
				strcpy(this->cookie, token);
				break;
			case id::accept:
				strcpy(this->accept, token);
				break;
			case id::accept_encoding:
				strcpy(this->accept_encoding, token);
				break;
			case id::accept_language:
				strcpy(this->accept_language, token);
				break;
			case id::accept_charset:
				strcpy(this->accept_charset, token);
				break;
			case id::content_length:
				this->content_length = atol(token);
				break;
			}
		}
	}

	//while(strtok(NULL,delims) != NULL);

	delete [] t_str;

	return 0;
}

int CHttpParser::get_param(const char *str, const char *param_name, char *param_val, int size_param_val)
{
	int len_str = strlen(str) + 1;
	int len_param_name = strlen(param_name) + 1;
	const char *offset = NULL;
	char *buf = NULL; 
	int result = 0;

	if(param_val == NULL)
		buf = new char [MAX_BUF];
	else
		buf = param_val;

	offset = strstr(str, param_name);
	if(offset == NULL)
		return -1;

	offset = offset + len_param_name;

	int count = 0;
	while(1)
	{
		if(count >= MAX_BUF)
			return -1;

		buf[count] = offset[count];
		if((offset[count] == '&')||(offset[count] == '\0'))
		{
			buf[count] = '\0';
			break;
		}

		count++;
	}

	if(strcmp(buf, "on") == 0)
	{
		result = 1;
	}
	else
	{
		result = atoi(buf);
	}

	if(!size_param_val)
		delete [] buf;

	return (!result)?-1:result;
}

void CHttpParser::print_data()
{
	printf("Method: %s\n", this->method);
	printf("URI: %s\n", this->URI);
	printf("Host: %s\n", this->host);
	printf("User-Agent: %s\n", this->user_agent);
	printf("Cookie: %s\n", this->cookie);
	printf("Accept: %s\n", this->accept);
	printf("Accept-Encoding: %s\n", this->accept_encoding);
	printf("Accept-Language: %s\n", this->accept_language);
	printf("Accept-Charset: %s\n", this->accept_charset);
	printf("Content-Length: %d\n", this->content_length);
}

void CHttpParser::clear()
{
	memset(method, 0, 10);
	memset(URI, 0, MAX_BUF);
	memset(host, 0, MAX_BUF);
	memset(user_agent, 0, MAX_BUF);
	memset(cookie, 0, MAX_BUF);
	memset(accept, 0, MAX_BUF);
	memset(accept_encoding, 0, MAX_BUF);
	memset(accept_language, 0,MAX_BUF);
	memset(accept_charset, 0,MAX_BUF);
	this->content_length = 0;

	if(header_buf)
	{
		delete [] header_buf;
		header_buf = NULL;
		
		header_buf_alloc = false;
	}
}

int get_size_array(const char **arr)
{
	int i = 0;

	for(;true;i++)
	{
		if(arr[i] == NULL)
			break;
	}

	return i-1;
};

CURL::CURL()
{
	URI = NULL;
	extension = NULL;
	params = NULL;
}

CURL::~CURL()
{
	if(URI)
	{
		delete [] URI;
	}
	
	if(extension)
	{
		delete [] extension;
	}
	
	if(params)
	{
		delete [] params;
	}
}

void CURL::parse(char *URL)
{
	char *token = NULL;
	int len = 0;

	token = strtok(URL, "&");
	len = strlen(token);
	if(len == 0)
		return;

	this->URI = new char[len + 1];

	strcpy(this->URI, token);

	token = strtok(NULL, "&");
	len = strlen(token);
	if(len == 0)
		return;

	this->params = new char[len + 1];

	strcpy(this->params, token);

	int i = 0,  j = 0;

	len = strlen(URI);
	this->extension = new char [6];

	memset(this->extension, 0, 6);

	for(j = len; j>=0; j++)
	{
		if((this->URI[j] == '/') || (j == 0) )
			return;

		if(this->URI[j] == '.')
			break;
	}

	i = 0;

	while(this->URI[j] != '\0')
		this->extension[i] = this->URI[j];
}

char *CURL::get_URI()
{
	return this->URI;
}

char *CURL::get_params()
{
	return this->params;
}

char *CURL::get_extension()
{
	return this->extension;
}
